`define SAFE_DESIGN

module fir_bandpass #(
    parameter integer N          = 63,   // FIR 阶数（奇数），总抽头数 M=N+1=64（偶数），线性相位II型
    // ========= 增益参数（线性增益 = GAIN_NUM / 2^GAIN_SHIFT）=========
    // +30 dB ≈ ×31.62 ： 31.62 = 32379 / 1024
    parameter integer GAIN_NUM   = 32379,
    parameter integer GAIN_SHIFT = 10
)(
    input                    clk,
    input                    rst_n,
    input                    en,             // 采样有效（每来一个新样本拉高1拍）
    input      signed [15:0] xin,            // 16-bit 输入（有符号）
    output reg signed [35:0] yout,           // 36-bit 累加输出（有符号，已含增益并饱和）
    output reg               yout_valid      // 与 yout 对齐（en 延迟 1 拍）
);
    // ------------ 基本参数 ------------
    localparam integer M     = N + 1;           // 抽头个数：64
    localparam integer HALF  = M/2;             // 32（对称前半）
    localparam integer CW    = 12;              // 系数位宽（Q11）
    localparam integer PW    = 17;              // 预加法位宽（16+16 -> 17）
    localparam integer MW    = PW + CW;         // 乘积位宽（29）
    localparam integer SW    = 36;              // 累加器位宽

    // ------------ 系数表（仅前半） ------------
    reg signed [CW-1:0] coeff [0:HALF-1];
    initial begin
        coeff[0]  = 12'sd0;
        coeff[1]  = 12'sd1;
        coeff[2]  = 12'sd3;
        coeff[3]  = 12'sd6;
        coeff[4]  = 12'sd10;
        coeff[5]  = 12'sd15;
        coeff[6]  = 12'sd20;
        coeff[7]  = 12'sd25;
        coeff[8]  = 12'sd29;
        coeff[9]  = 12'sd31;
        coeff[10] = 12'sd30;
        coeff[11] = 12'sd25;
        coeff[12] = 12'sd16;
        coeff[13] = 12'sd3;
        coeff[14] = -12'sd13;
        coeff[15] = -12'sd31;
        coeff[16] = -12'sd51;
        coeff[17] = -12'sd70;
        coeff[18] = -12'sd87;
        coeff[19] = -12'sd99;
        coeff[20] = -12'sd105;
        coeff[21] = -12'sd103;
        coeff[22] = -12'sd92;
        coeff[23] = -12'sd70;
        coeff[24] = -12'sd38;
        coeff[25] =  12'sd3;
        coeff[26] =  12'sd52;
        coeff[27] =  12'sd109;
        coeff[28] =  12'sd171;
        coeff[29] =  12'sd236;
        coeff[30] =  12'sd299;
        coeff[31] =  12'sd356;
    end

    // ------------ 输入移位寄存器（仅在 en=1 时推进） ------------
    reg signed [15:0] xbuf [0:M-1];  // 深度64：x[n], x[n-1], ... x[n-63]
    integer ii;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (ii=0; ii<M; ii=ii+1) xbuf[ii] <= 16'sd0;
        end else if (en) begin
            xbuf[0] <= xin;
            for (ii=0; ii<M-1; ii=ii+1) xbuf[ii+1] <= xbuf[ii];
        end
    end

    // ------------ 对称预加法（寄存，保持同一采样的一致性） ------------
    reg signed [PW-1:0] pair [0:HALF-1];
    integer pi;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (pi=0; pi<HALF; pi=pi+1) pair[pi] <= {PW{1'b0}};
        end else if (en) begin
            for (pi=0; pi<HALF; pi=pi+1)
                pair[pi] <= xbuf[pi] + xbuf[M-1-pi]; // x[n-k] + x[n-(M-1-k)]
        end
    end

    // ------------ 组合乘法 + 组合求和（同一拍完成） ------------
    wire signed [MW-1:0] prod [0:HALF-1];
    genvar gi;
    generate
        for (gi=0; gi<HALF; gi=gi+1) begin : GEN_MUL
            assign prod[gi] = pair[gi] * coeff[gi]; // 组合乘法（寄存的 pair）
        end
    endgenerate

    integer sj;
    reg signed [SW-1:0] sum_comb;
    always @* begin
        sum_comb = {SW{1'b0}};
        for (sj=0; sj<HALF; sj=sj+1) begin
            sum_comb = sum_comb + {{(SW-MW){prod[sj][MW-1]}}, prod[sj]};
        end
    end

    // ------------ 增益、饱和（组合），不增加时序级 ----------------
    // 36b × 常数 → 52b，再算术右移
    wire signed [51:0] y_mul    = $signed(sum_comb) * $signed(GAIN_NUM);
    wire signed [51:0] y_scaled = y_mul >>> GAIN_SHIFT;

    // 36-bit 饱和（阈值扩展到 52 位再比较）
    wire signed [51:0] SAT_MAX_52 = $signed({{16{1'b0}}, 36'h7FFFFFFFF}); // +2^35-1
    wire signed [51:0] SAT_MIN_52 = $signed({{16{1'b1}}, 36'h800000000}); // -2^35

    wire signed [35:0] y_sat =
        (y_scaled > SAT_MAX_52) ? 36'sh7FFFFFFFF :
        (y_scaled < SAT_MIN_52) ? 36'sh800000000 :
                                   y_scaled[35:0];

    // ------------ 输出寄存与有效对齐（en 延迟 1 拍） ------------
    reg en_d1;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            yout       <= {SW{1'b0}};
            yout_valid <= 1'b0;
            en_d1      <= 1'b0;
        end else begin
            yout       <= y_sat;   // 含增益与饱和后的结果
            en_d1      <= en;
            yout_valid <= en_d1;   // 与 yout 对齐
        end
    end

endmodule

// ===============================
// iir_biquad_df2t.v  (Q2.30 coef, Q1.31 state, Q1.15 I/O)
// Direct-Form II Transposed (更稳，资源省)
// ===============================
module iir_biquad_df2t #(
    // 系数 Q2.30（建议离线用 Python/Matlab 生成和量化）
    parameter signed [31:0] B0 = 32'sd0,
    parameter signed [31:0] B1 = 32'sd0,
    parameter signed [31:0] B2 = 32'sd0,
    parameter signed [31:0] A1 = 32'sd0,  // 注意：差分方程里是 “-a1*y - a2*y”
    parameter signed [31:0] A2 = 32'sd0
)(
    input  wire              clk,
    input  wire              rst_n,
    input  wire              en,          // 采样有效（48kHz 脉冲）
    input  wire signed [15:0] xin,        // Q1.15
    output reg  signed [15:0] yout,       // Q1.15
    output reg               yout_valid
);
    // 状态 Q1.31
    reg signed [31:0] z1, z2;

    // Q1.15 -> Q1.31
    wire signed [31:0] x_q31 = {xin,16'd0};

    // 乘法：Q2.30 * Q1.31 -> Q3.61，再 >>30 到 Q1.31
    function automatic signed [31:0] mul_q30_q31;
        input signed [31:0] c;  // Q2.30
        input signed [31:0] v;  // Q1.31
        reg   signed [63:0] p;
    begin
        p = c * v;
        mul_q30_q31 = p[61:30];
    end
    endfunction

    // 饱和到 Q1.31
    function automatic signed [31:0] sat_q31;
        input signed [63:0] v;
        localparam signed [31:0] MAX31 = 32'sh7FFFFFFF;
        localparam signed [31:0] MIN31 = -32'sh80000000;
    begin
        if (v > MAX31)      sat_q31 = MAX31;
        else if (v < MIN31) sat_q31 = MIN31;
        else                sat_q31 = v[31:0];
    end
    endfunction

    // Q1.31 -> Q1.15（带舍入）
    function automatic signed [15:0] q31_to_q15_sat;
        input signed [31:0] v31;
        reg   signed [32:0] r;  // 多 1bit 做舍入
    begin
        r = v31 + 32'sd1; // 极小正偏置避免负数右移陷阱，可按需改为四舍五入
        // 右移 16 位到 Q1.15
        if (v31[31:16] == 16'sh7FFF && v31[15:0] > 16'h7FFF) q31_to_q15_sat = 16'sh7FFF;
        else if (v31[31:16] == 16'sh8000 && v31[15:0] != 16'h0000) q31_to_q15_sat = -16'sh8000;
        else q31_to_q15_sat = v31[31:16];
    end
    endfunction

    // 组合计算
    wire signed [31:0] b0x = mul_q30_q31(B0, x_q31);
    wire signed [31:0] y_q31 = sat_q31($signed(z1) + $signed(b0x));

    wire signed [31:0] b1x = mul_q30_q31(B1, x_q31);
    wire signed [31:0] a1y = mul_q30_q31(A1, y_q31);
    wire signed [31:0] z1_n = sat_q31($signed(b1x) + $signed(z2) - $signed(a1y));

    wire signed [31:0] b2x = mul_q30_q31(B2, x_q31);
    wire signed [31:0] a2y = mul_q30_q31(A2, y_q31);
    wire signed [31:0] z2_n = sat_q31($signed(b2x) - $signed(a2y));

    reg en_d1;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            z1 <= 32'sd0; z2 <= 32'sd0;
            yout <= 16'sd0; en_d1 <= 1'b0; yout_valid <= 1'b0;
        end else begin
            if (en) begin
                z1 <= z1_n;
                z2 <= z2_n;
                yout <= q31_to_q15_sat(y_q31);
            end
            en_d1 <= en;
            yout_valid <= en_d1; // 1 拍延迟
        end
    end
endmodule


// iir2x_df2t.v  (两个 biquad 级联 + 末级增益)

module iir2x_df2t #(
    // Section 1
    parameter signed [31:0] S1_B0 = 32'sd0,
    parameter signed [31:0] S1_B1 = 32'sd0,
    parameter signed [31:0] S1_B2 = 32'sd0,
    parameter signed [31:0] S1_A1 = 32'sd0,
    parameter signed [31:0] S1_A2 = 32'sd0,
    // Section 2
    parameter signed [31:0] S2_B0 = 32'sd0,
    parameter signed [31:0] S2_B1 = 32'sd0,
    parameter signed [31:0] S2_B2 = 32'sd0,
    parameter signed [31:0] S2_A1 = 32'sd0,
    parameter signed [31:0] S2_A2 = 32'sd0,
    // Final gain (linear = GAIN_NUM / 2^GAIN_SHIFT)
    // +40 dB = ×100 → GAIN_NUM=100, GAIN_SHIFT=0
    parameter integer        GAIN_NUM   = 10,
    parameter integer        GAIN_SHIFT = 0
)(
    input  wire               clk,
    input  wire               rst_n,
    input  wire               en,          // 采样有效（48kHz）
    input  wire signed [15:0] xin,         // Q1.15
    output wire signed [15:0] yout,        // Q1.15
    output wire               yout_valid   // 与 yout 对齐
);
    // -------- 两段 biquad 级联（均为 Q1.15 I/O） --------
    wire signed [15:0] y1;
    wire               v1;

    iir_biquad_df2t #(
        .B0(S1_B0), .B1(S1_B1), .B2(S1_B2), .A1(S1_A1), .A2(S1_A2)
    ) u_sec1 (
        .clk(clk), .rst_n(rst_n), .en(en),
        .xin(xin), .yout(y1), .yout_valid(v1)
    );

    wire signed [15:0] y2;
    wire               v2;

    iir_biquad_df2t #(
        .B0(S2_B0), .B1(S2_B1), .B2(S2_B2), .A1(S2_A1), .A2(S2_A2)
    ) u_sec2 (
        .clk(clk), .rst_n(rst_n), .en(v1),
        .xin(y1), .yout(y2), .yout_valid(v2)
    );

    // -------- 末级线性增益（默认 +40 dB = ×100）--------
    // y2 为 Q1.15。用整数增益放大到 32 位，再可选右移，最后饱和回 Q1.15。
    wire signed [31:0] y2_mul    = $signed(y2) * $signed(GAIN_NUM); // 16xINT -> 32
    wire signed [31:0] y2_scaled = (GAIN_SHIFT==0) ? y2_mul : (y2_mul >>> GAIN_SHIFT);

    function automatic signed [15:0] sat16;
        input signed [31:0] v;
        begin
            if      (v >  32'sd32767)   sat16 =  16'sd32767;
            else if (v < -32'sd32768)   sat16 = -16'sd32768;
            else                        sat16 =  v[15:0];
        end
    endfunction

    assign yout       = sat16(y2_scaled);
    assign yout_valid = v2;

endmodule